#################################################################################
function Get-WinServiceForApp ($app)
{
  if ($app.EnvServices.count -ne 0)
  {
    foreach ( $ServiceEnv in $app.EnvServices )
    {
      if ($ServiceEnv.Env -eq $Env.Name)
     {
       if ($ServiceEnv.Winservice.length -gt 0)
       {
         return $ServiceEnv.WinService
       }
       if ($ServiceEnv.ClusterFQName.length -gt 0)
       {
         return $ServiceEnv.ClusterWinService
       }
     }
    }  
  }   
  return $app.WinService
}

#################################################################################
function Get-WebSiteForApp ($app)
{
  if ($app.EnvServices.count -ne 0)
  {
     foreach ( $ServiceEnv in $app.EnvServices )
    {
      if ($ServiceEnv.Env -eq $Env.Name)
      {
        return $ServiceEnv.WebSite
      }
    }  
  }   
  return $app.WebSite 
}

#################################################################################
function Get-AppPoolForApp ($app)
{
  if ($app.EnvServices.count -ne 0)
  {
     foreach ( $ServiceEnv in $app.EnvServices )
    {
      if ($ServiceEnv.Env -eq $Env.Name)
      {
        return $ServiceEnv.AppPool
      }
    }  
  }   

  return $app.AppPool
  
}
########### Query WebSite #######################################################
function Get-RemoteWebsiteStatus 
{
  param ( [string]$webSite, [string]$serverName )

  $FindSysytemRoot = Test-Path //$serverName/c$/winnt
  if ($FindSysytemRoot)
  {
    $wiis =  "c:\winnt\system32\iisweb.vbs"
  }
  else
  {
    $wiis =  "c:\windows\system32\iisweb.vbs"
  }
  #query service
	$errorOutput = [IO.Path]::GetTempFileName()
  [string]$queryService = psexec \\$serverName Cscript $wiis /s $serverName /query $webSite 2>$errorOutput
	$errorContent = Get-Content $errorOutput
	Remove-Item $errorOutput
	$errorContent | ?{($_ -match "Access is denied") -or ($_ -match "Couldn't access ")} | %{Throw $_}
	
  if ($queryService -match "STOPPED")
  {
    return "STOPPED"
  }
  if ($queryService -match "STARTED")
  {
    return "STARTED"
  }
  if ($queryService -match "Site\(s\) not found")
  {
    Throw "Could not find `'$webSite`' website on `'$serverName`'"
  }
	
  Throw "Unkown error occured while attempting to manage the `'$webSite`' Website on `'$serverName`'"
  
}

########### Query AppPool #######################################################
function Get-RemoteAppPoolStatus 
{
  param ( [string]$AppPool, [string]$serverName )

  # use base deployment path to determine iis location
  $adsutil =  $env:systemdrive + "\Inetpub\AdminScripts\adsutil.vbs"
  #query AppPool
	$errorOutput = [IO.Path]::GetTempFileName()
  [string]$queryAppPool = psexec \\$serverName Cscript $adsutil GET w3svc/AppPools/$AppPool/AppPoolState 2>$errorOutput
  $errorContent = Get-Content $errorOutput
	Remove-Item $errorOutput
	$errorContent | ?{($_ -match "Access is denied") -or ($_ -match "Couldn't access ")} | %{Throw $_}
  if ($queryAppPool -match "AppPoolState                    : \(INTEGER\) 4")
  {
    return "STOPPED"
  }
  if ($queryAppPool -match "AppPoolState                    : \(INTEGER\) 2")
  {
    return "STARTED"
  }
  if ($queryAppPool -match "\(GetObject Failed\)")
  {
    Throw "Could not find the `'$AppPool`' AppPool on `'$serverName`'"
  }
  Throw "Unkown error occured while attempting to manage the '`$AppPool`' AppPool on `'$serverName`'"
}
########### Query Service #######################################################
function Get-RemoteServiceStatus
{   
  param ( [string]$serviceName, [string]$serverName )

  #query service
  [string]$queryService = sc.exe \\$serverName query $serviceName
  
  if ($queryService -match "STOPPED")
  {
    return "STOPPED"
  }
  if ($queryService -match "RUNNING")
  {
    return "RUNNING"
  }
  if ($queryService -match "STOP_PENDING")
  {
    return "STOP_PENDING"
  }
  if ($queryService -match "START_PENDING")
  {
    return "START_PENDING"
  }
  if ($queryService -match "specified service does not exist")
  {
    Throw "Could not find the `'$serviceName`' Windows Service on `'$serverName`'."
  }
   Throw "Error occured while attempting to manage the `'$serviceName`' WinService on `'$serverName`' $queryService"
}

## Stop websites and services ###################################################

function Stop-ServicesOnServer
{
  param ([string]$serviceName,[string]$serverName, [string]$webSite, [string]$appPool ) 
  if ($PerformStop)
    {    
    if( $webSite.length -gt 0 )
    {
      $websiteStatus = Get-RemoteWebsiteStatus $webSite $serverName 
      $msg = "Stopping `'$($website)`' website on Server `'$($serverName)`'" 
			$msgStatus = $msg + " - Status: " + "[$websiteStatus]"
      if (Should-Process $msgStatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
				Log-Message $msg -nonewline
        if ($websiteStatus -eq "STARTED")
        {
          $wiis =  $env:windir + "\system32\iisweb.vbs"
          $Null = psexec \\$serverName Cscript $wiis /s $serverName /stop $webSite 2>$Null
          #refresh status
          $checkState = Get-RemoteWebsiteStatus $webSite $serverName 
          $RetryCnt = 0
          $RetryLimit = 10
          $RetryDelay = 2
          while ( ($checkState -ne "STOPPED") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteWebsiteStatus $webSite $serverName 
          }
          if ($checkState -ne "STOPPED")
          {
            Log-Message "`t [FAILED]" -foregroundcolor red
            Throw "The `'$website`' website on `'$serverName`' could not be STOPPED."
          }
          else
          {
            Log-Message "`t[STOPPED]" -foregroundcolor green
          }
        }
        else
        {
          Log-Message "`t[Already STOPPED]" -foregroundcolor yellow
        }
      }
    }
   ##### AppPool #####
   
   if( $AppPool.length -gt 0 )
    {
      $AppPoolStatus = Get-RemoteAppPoolStatus $AppPool $serverName  
      $msg = "Stopping `'$AppPool`' AppPool on Server `'$serverName`'"
			$msgstatus = $msg + " - Status: " + "[$AppPoolStatus]"
      if (Should-Process $msgstatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
				Log-Message $msg -nonewline
        if ($AppPoolStatus -eq "STARTED")
        {
          $adsutil =  "C:\Inetpub\AdminScripts\adsutil.vbs"
          $Null = psexec \\$serverName Cscript $adsutil stop_server w3svc/AppPools/$AppPool 2>$Null
          #refresh status
          $checkState = Get-RemoteAppPoolStatus $AppPool $serverName 
          $RetryCnt = 0
          $RetryLimit = 10
          $RetryDelay = 2
          while ( ($checkState -ne "STOPPED") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteAppPoolStatus $AppPool $serverName 
          }
          if ($checkState -ne "STOPPED")
          {
            Log-Message "`t[FAILED]" -foregroundcolor red
            Throw "The `'$AppPool`' AppPool on `'$serverName`' could not be STOPPED."
          }
          else
          {
            Log-Message "`t[STOPPED]" -foregroundcolor green
          }
        }
        else
        {
          Log-Message "`t[Already STOPPED]" -foregroundcolor yellow
        }
      }
    }
   ##### WinService ######
       
    if( $serviceName.length -gt 0 )
    {
      $ServiceStatus = Get-RemoteServiceStatus $serviceName $serverName 
      $msg = "Stopping Service `'$serviceName`' on Server `'$serverName`'"
			$msgstatus = $msg + " - Status: " + "[$ServiceStatus]"
      if (Should-Process $msgstatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
				Log-Message $msg -nonewline
        if ($ServiceStatus -eq "RUNNING")
        {
          $status = sc.exe \\$serverName stop $serviceName     
          #refresh status
          $checkState = Get-RemoteServiceStatus $serviceName $serverName
          $RetryCnt = 0
          $RetryLimit = 10
          $RetryDelay = 2
          while ( ($checkState -ne "STOPPED") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteServiceStatus $serviceName $serverName
          }
          if ($checkState -ne "STOPPED")
          {
            Log-Message "`t[FAILED]" -foregroundcolor red
            write-error "Service $($app.WinService) on $($serverName) could not be STOPPED."
          }
					else
					{
					  Log-Message "`t[STOPPED]" -foregroundcolor green      
					}     
        }
        else
        {
          Log-Message "`t[ALREADY STOPPED]" -foregroundcolor yellow
        }
      }
    }
  }
}

#### Start websites and services ################################################
function Start-ServicesOnServer #{$myinvocation}
{
  param ([string]$serviceName,[string]$serverName, [string]$webSite, [string]$AppPool )
  
  if ( $PerformStart )
  {
    if( $webSite.length -gt 0 )
    {
      $websiteStatus = Get-RemoteWebsiteStatus $webSite $serverName 
      $msg = "Starting `'$website`' website on Server `'$serverName`'"
			$msgstatus = $msg + " - Status: [$websiteStatus]"
      if (Should-Process $msgstatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
        Log-Message $msg -nonewline
        if ($websiteStatus -eq "STOPPED")
        {
          $wiis =  $env:windir + "\system32\iisweb.vbs"
          $Null = psexec \\$serverName Cscript $wiis /s $serverName /start $webSite 2>$Null
          #refresh status
          $checkState = Get-RemoteWebsiteStatus $webSite $serverName 
          $RetryCnt = 0
          $RetryLimit = 10
          $RetryDelay = 2
          while ( ($checkState -ne "STARTED") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteWebsiteStatus $webSite $serverName 
          }
          if ($checkState -ne "STARTED")
          {
            Log-Message "`t[FAILED]" -foregroundcolor red
            Throw "The `'$website`' website on `'$serverName`' could not be."
          }
          else
          {
         	  Log-Message "`t[STARTED]" -foregroundcolor green            
          }
        }
        else
        {
          Log-Message "`t[ALREADY STARTED}" -foregroundcolor yellow
        }
      }
    }
   ##### AppPool #####
   
   if( $AppPool.length -gt 0 )
    {
      $AppPoolStatus = Get-RemoteAppPoolStatus $AppPool $serverName 
      $msg = "Starting `'$AppPool`' AppPool on Server `'$serverName`'" 
			$msgStatus = $msg + " - Status: [$AppPoolStatus]"
      if (Should-Process $msgStatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
        Log-Message  $msg -nonewline
        if ($AppPoolStatus -eq "STOPPED")
        {
          $adsutil =  "C:\Inetpub\AdminScripts\adsutil.vbs"
          $Null = psexec \\$serverName Cscript $adsutil start_server w3svc/AppPools/$AppPool 2>$Null
          #refresh status
          $checkState = Get-RemoteAppPoolStatus $AppPool $serverName 
          $RetryCnt = 0
          $RetryLimit = 10
          $RetryDelay = 2
          while ( ($checkState -ne "STARTED") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteAppPoolStatus $AppPool $serverName 
          }
          if ($checkState -ne "STARTED")
          {
            Log-Message "`t[FAILED]" -foregroundcolor red
            Throw "The `'$AppPool`' AppPool on `'$serverName`' could not be Started."
          }
          else
          {
            Log-Message "`t[STARTED]" -foregroundcolor green
          }
        }
        else
        {
          Log-Message "`t[Already STARTED]" -foregroundcolor yellow
        }
      }
    }
   ##### WinService ######
   
    if( $serviceName.length -gt 0 )
    {
      $ServiceStatus = Get-RemoteServiceStatus $serviceName $serverName 
       
      $msg = "Starting Service `'$serviceName`' on Server `'$serverName`'"
			$msgStatus = $msg +  " - Status: [$ServiceStatus]"
      if (Should-Process $msgStatus ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
        Log-Message $msg -nonewline
        if ($ServiceStatus -eq "STOPPED")
        {
          $status = sc.exe \\$serverName start $serviceName      
          #refresh status
          $checkState = Get-RemoteServiceStatus $serviceName $serverName
          $RetryCnt = 0
          $RetryLimit = 50
          $RetryDelay = 5
          while ( ($checkState -ne "RUNNING") -and ($RetryCnt -lt $RetryLimit) )
          {
            $RetryCnt += 1
            start-sleep -seconds $RetryDelay
            # Refresh Status
            $checkState = Get-RemoteServiceStatus $serviceName $serverName
          }
            if ($checkState -ne "RUNNING")
            {
              Log-Message "`t[FAILED]" -foregroundcolor red
              Throw "Service $($app.WinService) on $($serverName) could not be STARTED."
            }
            else
            {
              Log-Message "`t[RUNNING]" -foregroundcolor green
            }                      
        }  
        else
        {
          Log-Message "`t[ALREADY RUNNING]" -foregroundcolor yellow            
        }
      }
    }
  }
}

#######################################################################
#  Windows Service Management Function 
#######################################################################

function Manage-WinServices
{
  param($StartOrStop)

  $all = Collect-Services $all 
  #### collect services ####
  $ServersToServices = @{} 
  foreach ($applications in $all)
  { 
    foreach ($server in $applications.envServers )
    { 
      if ($server.ClusterFQName -ne "" -and $server.ClusterFQName -ne $null ) 
      {
         if (!($WinServiceServers -contains $server.ClusterFQName))
        {
					$ServerKey = $server.ClusterFQName
					$WinServices=@()
					if(!($WinServices -contains $Server.WinService ))
					{
						$WinServices += $Server.WinService
					}
					$ServersToServices.$ServerKey += $WinServices		
        }  
      }
      if ($server.WinService.length -gt 0 -or $applications.WinService.length -gt 0)
      {
        if ($server.ClusterFQName.length -lt 1)
        {
					$ServerKey = $server.FQName
					$WinServices=@()
					if(!($WinServices -contains $Server.WinService ))
					{
						$WinServices += $Server.WinService
					}
					$ServersToServices.$ServerKey += $WinServices		
        }  
      }  
    }      
  }

	foreach ($serverkey in $ServersToServices.keys)
  {
		
		foreach ($winservice in $ServersToServices[$serverkey])
		{
		  if ($StartOrStop -eq "Stop")
      {
         Stop-ServicesOnServer -serviceName $winservice -serverName $serverkey
      }
      if ($StartOrStop -eq "Start")
      {
        Start-ServicesOnServer -serviceName $winservice -serverName $serverkey
      }  
      
		}       
  } 
  
   #### End Windows Service Management Function ##########################
}  

#######################################################################
#  Web Site Service Management Function 
#######################################################################

function Manage-WebSites
{
  param($StartOrStop)
  $all = Collect-Services $all 
  ## collect sites ####
  $sites = @()
  foreach ($applications in $all)
  {
    foreach ($server in $applications.envServers)
    { 
      
      if ($applications.website.length -gt 0 -and $server.WebSite.Length -lt 1 -and $server.WinService.Length -lt 1)
      {
        
        if (!($sites -contains $applications.website))
        {
          $sites += $applications.website
        } 
      }
      
      if ($server.WebSite.length -gt 0)
      {
        if (!($sites -contains $server.WebSite))
        {
          $sites += $server.WebSite
        }  
      }  
    }
  }  
  ## collect Web Servers #########################
  $WebServers = @()
  foreach ($applications in $all)
  {
    foreach ($server in $applications.envServers)
    {        
      if ($server.WebSite.length -gt 0 -or $applications.website.length -gt 0)
      {
        if (!($WebServers -contains $server.FQName))
        {
          $WebServers += $server.FQName
        }  
      }  
    }      
  }
  foreach ($server in $WebServers)
  {
      foreach ($site in $sites)
      {
        if ($StartOrStop -eq "Stop")
        {
          Stop-ServicesOnServer -webSite $site -serverName $server
        }
        if ($StartOrStop -eq "Start")
        {
          Start-ServicesOnServer -webSite $site -serverName $server
        }
        
      }
   } 
}  
#######################################################################
#  AppPool Management Function 
#######################################################################

function Manage-AppPools
{
  param($StartOrStop)
  $all = Collect-Services $all 
  ## collect sites ####
  $pools = @()
  foreach ($applications in $all)
  {
    foreach ($server in $applications.envServers)
    { 
      
      if ($applications.AppPool.length -gt 0 -and $server.AppPool.Length -lt 1 -and $server.WinService.Length -lt 1)
      {
        
        if (!($pools -contains $applications.AppPool))
        {
          $pools += $applications.AppPool
        } 
      }
      
      if ($server.AppPool.length -gt 0)
      {
        if (!($pools -contains $server.AppPool))
        {
          $pools += $server.AppPool
        }  
      }  
    }
  }  
  ## collect Web Servers #########################
  $AppPoolServers = @()
  foreach ($applications in $all)
  {
    foreach ($server in $applications.envServers)
    {        
      if ($server.AppPool.length -gt 0 -or $applications.AppPool.length -gt 0)
      {
        if (!($AppPoolServers -contains $server.FQName))
        {
          $AppPoolServers += $server.FQName
        }  
      }  
    }      
  }
  foreach ($server in $AppPoolServers)
  {
      foreach ($pool in $pools)
      {
        if ($StartOrStop -eq "Stop")
        {
          Stop-ServicesOnServer -appPool $pool -serverName $server
        }
        if ($StartOrStop -eq "Start")
        {
          Start-ServicesOnServer -appPool $pool -serverName $server
        }
        
      }
   } 
}  

function Recycle-IIS
{
	if (-not [system.String]::IsNullOrEmpty($env.IISReset))
	{
		if (-not (Test-Path $env:windir\system32\iisreset.exe))
		{
		  Log-Message "IISRESET.EXE NOT FOUND ON LOCAL MACHINE - Please wait while remote execution is invoked."
			$IISresetLocal = $FALSE
		}
		else
		{
		  $IISresetLocal = $TRUE
		}
		foreach ($server in $env.servers)
		{
			Log-Message $MinorLine
			$msg = "IISRESEST on $($server.fqname)"
      if (Should-Process $msg ([REF]$AllAnswer) "IISReset" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
      {
				Log-Message $msg
				if ($IISresetLocal)
				{
				  iisreset $server.fqname
				}
				else
				{
					$iisreset = Find-IISRESET $server
				  psexec "\\$($server.fqname)" "$iisreset" 2>$Null
		  	}
				#
			}
			else
			{
			  if ($IISresetLocal)
				{
				  iisreset $server.fqname /status
				}
				else
				{
				  $iisreset = Find-IISRESET $server
				  psexec "\\$($server.fqname)" "$iisreset" '/status' 2>$Null
				}
			}
		}
	}

}

function Find-IISRESET ($server)
{
	if (Test-Path "\\$($server.fqname)\c$\windows\system32\iisreset.exe")
	{
		$iisreset =  "c:\windows\system32\iisreset.exe"
	}
	elseif (Test-Path "\\$($server.fqname)\c$\winnt\system32\iisreset.exe") 
	{
		$iisreset =  "c:\winnt\system32\iisreset.exe"
	}
	else
	{
	  Throw "Unable to Locate IISRESET on $($server.fqname)."
	}
	$iisreset
}
#######################################################################
#  App_offline.htm
#######################################################################
function App-offline
{
#if a file called app_offline exists in the intall directory then copy it to
# designated server and application path
# at the end of the deployment remove the file
# this requires that the app_offline.htm file is not deleted during the deployment
	param($StartOrStop)
	$ConfigFile = (join-path (Get-ScriptDir) "..\DeploymentEnv.xml")
	$deploymentenvxml = ReadXmlFileIntoObjectTree $ConfigFile -NoExpansion
	$deploymentapps = $deploymentenvxml.applications
	$deploymentenvs = $deploymentenvxml.environments
	$serversOffline = @()
	foreach ($app in $deploymentapps.application)
	{
		foreach ($appenv in $($app.targetenvironments))
		{
			
			foreach ($appenvTargetenv in $appenv.Targetenvironment)
			{
				if ($appenvTargetenv.name -eq $TargetEnv)
				{
					foreach ($appserver in $appenvTargetenv.targetservers)
					{	
						[array]$targetappservers = $appserver.targetserver
						foreach ($targetappserver in $targetappservers)
						{
							foreach($deploymentenv in $deploymentenvs)
							{
								$deploymentenvVals = $deploymentenv.environment
								foreach ($DeployEnvName in $deploymentenvVals)
								{
									if ($DeployEnvName.name -eq $targetenv)
									{
										foreach ($server in $($DeployEnvName.servers.server))
										{
											if ($server.name -eq $targetappserver.name)
											{
												$servervolume = $server.volume
											}
										}
									}
								}
							
							}
							$targetServerAppRoot = "\\$($targetappserver.name)\$servervolume\$($app.destfolder)"
							if (!($serversOffline -contains $targetServerAppRoot) -and $targetappserver.app_offline -eq "True" )
							{
								$serversOffline += $targetServerAppRoot
							}
						}
					}
				}
			}
		
		}
	}
	#check to see if an app_offline.htm file exists in the install folder... If it does not throw a message
	#check if any servers had the app_offline attribute by checking if the $serversOffline array is not empty
	
	if (Test-Path "./Install/App_offline.htm") 
	{
		foreach ($AppOfflinePath in $serversOffline)
		{
			if ($StartOrStop -eq "Stop")
			{
				$msg = "Copying App_offline.htm to $AppOfflinePath."
				if (Should-Process $msg ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
				{
					log-message $msg -nonewline
					Copy-Item "./Install/App_offline.htm" $AppOfflinePath
					log-message "`t[Website Offline]" -fore yellow
					
				}
			}
			if ($StartOrStop -eq "Start")
			{
				$msg = "Removing App_offline.htm from $AppOfflinePath."
				if (Should-Process $msg ([REF]$AllAnswer) "" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)
				{
					log-message $msg -nonewline
					Remove-Item $AppOfflinePath\App_Offline.htm -force
					$msg += " - Website online"
					log-message "`t[Website Online]" -fore green
				}
			}
		} 
	}
	elseif ($serversOffline.count -gt 0)
	{
		Log-Error "No App_offline.htm file found in the Install folder of the deployment package"
	}
}